Skip to content

Per module lm history #8199

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
May 22, 2025
Merged

Conversation

chenmoneygithub
Copy link
Collaborator

Make it possible to do module.inspect_history, which gives the LM history of the module. In nesting case, the same LM history message will be available to all the ancestor callers.

Example code:

import dspy


class MyProgram(dspy.Module):
    def __init__(self):
        super().__init__()
        self.cot = dspy.ChainOfThought("question -> answer")

    def forward(self, question: str, **kwargs) -> str:
        return self.cot(question=question)


dspy.settings.configure(lm=dspy.LM("openai/gpt-4o-mini", cache=False), adapter=dspy.JSONAdapter())
program = MyProgram()
program(question="What is the capital of France?")

program.inspect_history()

It will give:

[2025-05-08T14:21:06.581570]

System message:

Your input fields are:
1. `question` (str)
Your output fields are:
1. `reasoning` (str)
2. `answer` (str)
All interactions will be structured in the following way, with the appropriate values filled in.

Inputs will have the following structure:

[[ ## question ## ]]
{question}

Outputs will be a JSON object with the following fields.

{
  "reasoning": "{reasoning}",
  "answer": "{answer}"
}
In adhering to this structure, your objective is: 
        Given the fields `question`, produce the fields `answer`.


User message:

[[ ## question ## ]]
What is the capital of France?

Respond with a JSON object in the following order of fields: `reasoning`, then `answer`.


Response:

{"reasoning":"The capital of France is a well-known fact, commonly taught in geography. Paris is recognized globally as the capital city, serving as the political, economic, and cultural center of the country.","answer":"Paris"}

You can also access the history by program.cot.inspect_history.

@chenmoneygithub chenmoneygithub requested a review from okhat May 21, 2025 23:56
@@ -17,10 +17,10 @@

class Predict(Module, Parameter):
def __init__(self, signature, callbacks=None, **config):
super().__init__(callbacks=callbacks)
Copy link
Collaborator

@okhat okhat May 22, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Btw typical custom DSPy programs don't call super().__init__(...).

Will that be an issue?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nothing will crash, but they will lose some perks from dspy.Module. We can follow what PyTorch does - recommending calling super().__init__() when customizing modules: https://docs.pytorch.org/tutorials/beginner/examples_nn/polynomial_module.html#pytorch-custom-nn-modules

@@ -20,26 +21,38 @@ def _base_init(self):
def __init__(self, callbacks=None):
self.callbacks = callbacks or []
self._compiled = False
# LM calling history of the module.
self.history = []
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cool! Does this work if there are multiple LMs used in the module for different sub-modules?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes! This per-module history works the same for multi-lm situation.

Just tested with the following code and no issue:

import dspy


class MyProgram(dspy.Module):
    def __init__(self):
        super().__init__()
        self.cot = dspy.ChainOfThought("question -> answer")
        self.cot2 = dspy.ChainOfThought("question, answer -> judgement")
        self.cot2.predict.lm = dspy.LM("openai/gpt-4o", cache=False)

    def forward(self, question: str, **kwargs) -> str:
        answer = self.cot(question=question).answer
        return self.cot2(question=question, answer=answer)


dspy.settings.configure(lm=dspy.LM("openai/gpt-4o-mini", cache=False), adapter=dspy.JSONAdapter())
program = MyProgram()
program(question="What is the capital of France?")

print(program.history)

@okhat okhat merged commit 1e8d3e2 into stanfordnlp:main May 22, 2025
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants